using System;
using System.Collections.Generic;
using System.Text;
using LibGreen.Jabber.Connection;
using LibGreen.Jabber.Delegates;
using LibGreen.Jabber.Managers;

namespace LibGreen.Jabber
{
    /// <summary>
    /// Encapsulates an XMPP session
    /// </summary>
    public class Session
    {
        private readonly string server;
        private readonly int port;
        private readonly string domain;
        private Jid jid;
        private string password;
        private XmppConnection serverConnection;
        private readonly PresenceManager presenceManager;
        private readonly RosterManager rosterManager;
        private readonly MessageManager messageManager;
        private ProxiedSocketFactoryDelegate socketFactory;

        #region Event handlers
        /// <summary>
        /// Raised on an update of the login process' status
        /// </summary>
        public event ConnectionUpdatedHandler LoginProgress;

        /// <summary>
        /// Raises the <see cref="LoginProgress"/> event
        /// </summary>
        /// <param name="sender">The <see cref="XmppConnection"/> used for login</param>
        /// <param name="e">A description of the status change event</param>
        protected internal void OnLoginProgress(object sender, ConnectionUpdatedEventArgs e)
        {
            if (LoginProgress != null)
            {
                LoginProgress(this, e);
            }
        }

        /// <summary>
        /// Raised when the login process has completed
        /// </summary>
        public event EventHandler LoginComplete;

        /// <summary>
        /// Raises the <see cref="LoginProgress"/> event
        /// </summary>
        /// <param name="sender">The <see cref="XmppConnection"/> used for login</param>
        /// <param name="e">An <see cref="EventArgs"/> object</param>
        protected internal void OnLoginComplete(object sender, EventArgs e)
        {
            XmppConnection connection = sender as XmppConnection;
            connection.ConnectionUpdated -= LoginProgress;
            connection.ConnectionFinished -= OnLoginComplete;
            connection.ConnectionFailed -= OnLoginFailed;

            if (LoginComplete != null)
            {
                LoginComplete(this, e);
            }
        }

        /// <summary>
        /// Raised when the login connection has failed
        /// </summary>
        public event ConnectionFailedHandler LoginFailed;

        /// <summary>
        /// Raises the <see cref="LoginFailed"/> event
        /// </summary>
        /// <param name="sender">The <see cref="XmppConnection"/> used for login</param>
        /// <param name="e">A description of the failure</param>
        protected internal void OnLoginFailed(object sender, ConnectionFailedEventArgs e)
        {
            XmppConnection connection = sender as XmppConnection;
            connection.ConnectionUpdated -= LoginProgress;
            connection.ConnectionFinished -= OnLoginComplete;
            connection.ConnectionFailed -= OnLoginFailed;

            if (LoginFailed != null)
            {
                LoginFailed(this, e);
            }
        }
        #endregion

        /// <summary>
        /// Initializes a new session on the specified server
        /// </summary>
        /// <param name="server">The XMPP server to which to connect</param>
        /// <param name="domain">The domain name of the server</param>
        /// <param name="port">The port to which to connect</param>
        public Session(string server, string domain, int port)
        {
            this.server = server;
            this.domain = domain;
            this.port = port;

            presenceManager = new PresenceManager(this);
            rosterManager = new RosterManager(this);
            messageManager = new MessageManager(this);

            socketFactory = XmppConnection.DirectSocketConnectionFactory;
        }

        /// <summary>
        /// Connects the session to the server with the specified username and password
        /// </summary>
        /// <param name="username">The username used to connect to the server</param>
        /// <param name="password">The password used to access the server account</param>
        public void Connect(string username, string password)
        {
            Connect(username, password, "");
        }

        /// <summary>
        /// Connects the session to the server with the specified username and password,
        /// and requests the specified resource
        /// </summary>
        /// <param name="username">The username used to connect to the server</param>
        /// <param name="password">The password used to access the server account</param>
        /// <param name="resource">The requested server resource, which can be blank</param>
        public void Connect(string username, string password, string resource)
        {
            this.password = password;

            jid = new Jid(username, domain, resource);

            serverConnection = new XmppConnection(this, server, port);
            serverConnection.ConnectionUpdated += new ConnectionUpdatedHandler(LoginProgress);
            serverConnection.ConnectionFailed += new ConnectionFailedHandler(OnLoginFailed);
            serverConnection.ConnectionFinished += new ConnectionFinishedHandler(OnLoginComplete);
            serverConnection.ConnectToServer();
        }

        /// <summary>
        /// Disconnects the session from the server
        /// </summary>
        public void Disconnect()
        {
            Presence.CurrentPresence = Jabber.ProtocolElements.Presence.Unavailable;
            serverConnection.Disconnect();
            jid = null;
        }

        #region Public properties
        /// <summary>
        /// Gets a value indicating whether this session is connected to the server or not
        /// </summary>
        public bool IsConnected
        {
            get { return (ServerConnection != null) ? (ServerConnection.State == ConnectionState.Connected) : false; }
        }

        /// <summary>
        /// Gets the server to which to connect
        /// </summary>
        public string Server
        {
            get { return server; }
        }

        /// <summary>
        /// Gets the port to which to connect
        /// </summary>
        public int Port
        {
            get { return port; }
        }

        /// <summary>
        /// Gets the JID assigned to the session
        /// </summary>
        public Jid JID
        {
            get { return jid; }
        }

        /// <summary>
        /// Gets the <see cref="PresenceManager"/> for this session
        /// </summary>
        public PresenceManager Presence
        {
            get { return presenceManager; }
        }

        /// <summary>
        /// Gets the <see cref="RosterManager"/> for this session
        /// </summary>
        public RosterManager Roster
        {
            get { return rosterManager; }
        }

        /// <summary>
        /// Gets the <see cref="MessageManager"/> for this session
        /// </summary>
        public MessageManager Messages
        {
            get { return messageManager; }
        }

        /// <summary>
        /// Gets or sets the <see cref="ProxiedSocketFactoryDelegate"/> to use to create new socket connections
        /// </summary>
        /// <remarks>By default, this property is set to <see cref="Session.DirectSocketConnectionFactory"/></remarks>
        public ProxiedSocketFactoryDelegate ProxiedSocketFactory
        {
            get { return socketFactory; }
            set { socketFactory = value; }
        }
        #endregion

        /// <summary>
        /// Gets the password used to access the server account
        /// </summary>
        protected internal string Password
        {
            get { return password; }
        }

        /// <summary>
        /// Gets the <see cref="XmppConnection"/> supporting this session
        /// </summary>
        protected internal XmppConnection ServerConnection
        {
            get { return serverConnection; }
        }
    }
}
